using System;
using System.Collections.Generic;
using com.bodurov.SilverlightControls.XmlCodeEditor.Enums;

namespace com.bodurov.SilverlightControls.XmlCodeEditor
{
    internal class TokenizationState
    {


        private readonly List<ListOfTokens> _linesOfTokens = new List<ListOfTokens>();
        private ListOfTokens _lastLineOfTokens;
        private readonly string[] _lines;
        private bool _lineAlreadyParsed;

        public TokenizationState(string content) : this(content, 0, XmlArea.OutsideTag) { }
        public TokenizationState(string content, int currentIndex) : this (content, currentIndex, XmlArea.OutsideTag) { }
        public TokenizationState(string content, int currentIndex, XmlArea currentArea)
        {
            this.SkipMovingToTheNext = true;
            Content = content;
            this._lines = (String.IsNullOrEmpty(Content)) ? new string[]{} : Content.Split('\n');
            this.LastCharIndex = CurrentCharIndex = currentIndex;
            this.CurrentArea = currentArea;
            this.CurrentLineIndex = -1;
        }

        public string Content { get; private set; }
        public IEnumerable<string> Lines { get { return this._lines; } }

        public int CurrentLineIndex { get; private set; }
        public int CurrentCharIndex { get; private set; }
        public int LastCharIndex { get; private set; }
        public bool SkipMovingToTheNext { get; set; }
        public bool EndHasBeenReached { get; set; }

        public XmlArea CurrentArea { get; set; }
        public XmlElement LastElement { get { return LastToken == null ? XmlElement.EndOfFile : LastToken.Type; } }
        public bool IsEndOfFile { get { return LastToken == null ? false : LastToken.Type == XmlElement.EndOfFile; } }
        public Token LastToken { get; set; }
        public string CurrentLine { get { return this._lines[CurrentLineIndex]; } }

        public ListOfTokens LastLineOfTokens { get { return this._lastLineOfTokens; } }
        public List<ListOfTokens> LinesOfTokens { get { return this._linesOfTokens; } }


        public char CurrentChar { get { return CurrentLine[CurrentCharIndex]; } }

        internal bool MoveToNextLine()
        {
            this._lineAlreadyParsed = false;
            if (this.CanMoveToNextLine())
            {
                this._lastLineOfTokens = new ListOfTokens();
                this.EndHasBeenReached = false;
                this.CurrentLineIndex++;
                this.LastCharIndex = this.CurrentCharIndex = 0;
                this.SkipMovingToTheNext = true;
                return true;
            }
            return false;
        }
        internal bool CanMoveToNextLine()
        {
            return ((CurrentLineIndex + 1) < this._lines.Length);
        }

        internal void LineHasBeenParsed()
        {
            if (this._lineAlreadyParsed) return;
            if (this._lastLineOfTokens == null)
            {
                throw new TokenizationException("Missing last line of tokens");
            }
            //Dbg.TraceForEach(this._lastLineOfTokens, (tokens, index) => string.Join("|", tokens.Content));
            this._linesOfTokens.Add(this._lastLineOfTokens);
            this._lineAlreadyParsed = true;
        }

        internal Token AddToken(XmlElement openingServerPrintScriptBracket, string currentToken)
        {
            return this.AddToken(openingServerPrintScriptBracket, this.CurrentCharIndex, this.CurrentCharIndex + currentToken.Length);
        }

        internal Token AddToken(XmlElement type, int startIndex, int endIndex)
        {
            if (startIndex > endIndex)
                throw new TokenizationException("Start index '{0}' cannot be greater or equal to the end index of '{1}'", startIndex, endIndex);
            //Dbg.Trace("XmlElement={0}", type);

            //var len = endIndex >= this.Content.Length ? this.Content.Length - 1 - startIndex : endIndex - startIndex;
            var len = endIndex - startIndex;
            var token =
                new Token
                {
                    Within = this.CurrentArea,
                    Type = type,
                    Content = len == 0 ? "" : this.CurrentLine.Substring(startIndex, len)
                };
            this._lastLineOfTokens.AddLast(token);
            this.LastToken = token;

            this.CurrentCharIndex = this.LastCharIndex = endIndex;
            this.SkipMovingToTheNext = true;

            return token;
        }




        internal bool MoveNext()
        {
            if (this.SkipMovingToTheNext)
            {
                this.SkipMovingToTheNext = false;
                return this.CurrentLine.Length > this.CurrentCharIndex;
            }
            this.CurrentCharIndex++;
            return (this.CurrentCharIndex < this.CurrentLine.Length);
        }


        internal bool CurrentLineHasOnlySpaceAfterIndex(int i)
        {
            var line = this.CurrentLine;
            if(String.IsNullOrEmpty(line)) throw new TokenizationException("Line is missing");
            if (i == 0 || i >= line.Length) throw new TokenizationException("Line does not contain index '{0}'", i);

            for(var j = i + 1; j < line.Length; j++)
            {
                if (!Char.IsWhiteSpace(line[j])) return false;
            }
            return true;
        }
    }
}